
import org.juiceshop.gm.midi.SimplePlayer

def FILENAME = "music.mid"
//def FILENAME = "playtest.mid"

DataInputStream dis = new DataInputStream(new FileInputStream(FILENAME))

def tracks = readHeader(dis)

tracks.times { readTrack(dis) }

dis.close()

SimplePlayer sp = new SimplePlayer()
def sequence = sp.loadSequence('music.mid')
//println "playing $sequence"
//sp.play sequence

def readHeader(DataInputStream dis) {
	assert dis.readInt() == 0x4D546864
	assert dis.readInt() == 6
	def format = dis.readShort()
	println format.class
	assert format>=0 && format<=2
	println "Format: $format"
	def numberOfTracks = dis.readShort()
	println "Number of tracks: $numberOfTracks"
	def timeDivision = dis.readShort()
	println "Time division: $timeDivision"
	numberOfTracks
}

def readTrack(DataInputStream dis) {
	assert dis.readInt() == 0x4D54726B
	def chunkSize = dis.readInt()
	println "Chunk size: $chunkSize"
	byte[] buffer = new byte[chunkSize]
	def bytes = dis.read(buffer)
	assert bytes==chunkSize
	println "Bytes read: $bytes"
	def iterator = buffer.iterator()
	def lastTick = 0
	while (iterator.hasNext()) {
		lastTick += readEvent(iterator,lastTick)
	}
	
}

def lastType = 0

def readEvent(Iterator it, lastTick) {
	int ticks = getVariable(it)
	print "${lastTick+ticks}-$ticks: "
	def eventType = it.next()
	def event
	if (eventType==(byte)0xFF) {
		def type = it.next()
		def size = getVariable(it)
		def data = []
		size.times { x -> data << it.next() }
		def name
		switch (type) {
		case 0x2f: name = "EndOfTrack"; break
		case 0x51: name = "SetTempo"; break
		case 0x58: name = "TimeSignature"; break
		case 0x59: name = "KeySignature"; break
		default: name = format("%02x",type)
		}
		println "MetaEvent type=$name size=$size data=$data"
		lastType = 0
	} else {
		def data1
		if (eventType & 0x80) {
			data1 = it.next()
			lastType = eventType
		} else {
			data1 = eventType
			eventType = lastType
		}
	    def channel = eventType&0xF
	    def type = eventType&0xF0
	    def name
	    switch(type) {
	    case 0x80: name = "NoteOff"; break
	    case 0x90: name = "NoteOn"; break
	    case 0xA0: name = "NoteAftertouch"; break
	    case 0xB0: name = "Controller"; break
	    case 0xC0: name = "ProgramChange"; break
	    case 0xD0: name = "ChannelAftertouch"; break
	    case 0xE0: name = "PitchBend"; break
	    default: name = format("%02x",type)
	    }
	    if (type==0xC0 || type==0xD0) {
	        println "ChannelEvent channel=$channel type=$name data=$data1"
	    } else {
	        def data2 = it.next()
	        println "ChannelEvent channel=$channel type=$name data=$data1,$data2"
	    }
	}
	ticks
}


int getVariable (Iterator it) {
	def b = it.next()
	int result = b & 0x7F
	while (b&0x80) {
		b = it.next()
		result = (result<<7) | ( b & 0x7f)
	}
	result
}

def format(String fmt, ...args) {
	new Formatter().format(fmt,args).toString()
}
